package operations;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.LinkedList;

import util.GAConfig;
import util.Randomness;

import model.Filter;
import model.TestClusterBuilder;


public class Copy_2_of_CreateArray extends Operation{
	  // State variables.
	private final String ref;
	  private  int length;
	  private final Class<?> elementType;
	  private final List<Object> inputVariables;
	  
	  private List<Class<?>> inputTypesCached;
	  private Class<?> outputTypeCached;    

	  public Copy_2_of_CreateArray(String ref,Class<?> elementType, int length,List<Object> inputVariables) {

		    // Check legality of arguments.
		    if (elementType == null) throw new IllegalArgumentException("elementType cannot be null.");
		    if (length < 0) throw new IllegalArgumentException("arity cannot be less than zero: " + length);

		    // Set state variables.
		    this.ref=ref;
		    this.elementType = elementType;
		    this.length = length;
		    this.inputVariables= new LinkedList<Object>(inputVariables);
		  }
		public static Copy_2_of_CreateArray generate(Class<?> cls,TestClusterBuilder TestCluster, String ref){
			Copy_2_of_CreateArray  array=null;


			   if (Filter.isPrimitiveOrStringType(cls.getComponentType())) 
				   array = randPrimitiveArray(cls, TestCluster,ref);
			   else
				   array= randNonPrimitiveArray(cls,TestCluster,ref);
			   return  array;
		}
		  private static Copy_2_of_CreateArray randNonPrimitiveArray(Class<?> cls,TestClusterBuilder TestCluster,String ref) {
			  //Object ref=generateReference(componentType, TestCluster);
			  if(ref==null)
				  ref=  getReference(cls.getName(),TestCluster.getRefFactory());
			  List<Object> inputs = new ArrayList<Object>();
			  Copy_2_of_CreateArray s = new Copy_2_of_CreateArray(ref,cls.getComponentType(), 0, inputs);
			  if  (ValuesGenerator.getBoolean(Operation.SET_TO_NULL))
				  return s;
			  else{
				    int length = Randomness.nextRandomInt(GAConfig.ArraySize);
				    for (int i = 0 ; i < length ; i++) {
				    	if(generateReference(cls.getComponentType(), TestCluster)==null)
				    		inputs.add(null);
				    	else
				    		inputs.add(Operation.getReference(generateReference(cls.getComponentType(), TestCluster).getName(),TestCluster.getRefFactory()));
				    }
				    s = new Copy_2_of_CreateArray(ref.toString(),cls.getComponentType(), length, inputs);
			  }
				  return s;
			  
		  }
		  private static Copy_2_of_CreateArray randPrimitiveArray(Class<?> cls,TestClusterBuilder TestCluster,String ref) {
			    assert cls.getComponentType().isPrimitive();
			    Copy_2_of_CreateArray s=null;
				  if(ref==null)
					  ref=  getReference(cls.getName(),TestCluster.getRefFactory());

			    int length = Randomness.nextRandomInt(GAConfig.ArraySize);
			    List<Object> inputs = new ArrayList<Object>();
			    for (int i = 0 ; i < length ; i++) {
			      inputs.add(ValuesGenerator.generatePrimitiveValue(cls.getComponentType()));
			    }
			    //Object ref=generateReference(cls, TestCluster);

			    if(ref!=null)
			    	s= new Copy_2_of_CreateArray(ref.toString(),cls.getComponentType(), length, inputs);
			    
			    return s;
			  }


	  public Class<?> getElementType() {
		    return this.elementType;
		  }
	  public String getReference() {
		    return this.ref;
		  }
	  public List<Object> getInputVariables() {
		    return this.inputVariables;
		  }
	  public int getLength() {
		    return this.length;
		  }
	  public void setLength(int Length){
		  if(Length>=0)
			  this.length=Length;
	  }
		  /**
		   * Extracts the input constraints for this ArrayDeclarationInfo
		   * @return list of input constraints
		   */
		  public List<Class<?>> getInputTypes() {
		    if (inputTypesCached == null) {
		      this.inputTypesCached = new ArrayList<Class<?>>(length);
		      for (int i = 0 ; i < length ; i++)
		        inputTypesCached.add(elementType);
		      inputTypesCached = Collections.unmodifiableList(inputTypesCached);
		    }
		    return Collections.unmodifiableList(this.inputTypesCached);
		  }
		  @Override
		    public String toString() {
			  StringBuilder sb = new StringBuilder();
			  sb.append(ref +"=new " +elementType.getName() + "[] " +" {");
			    for(int i=0;i<this.inputVariables.size();i++){
			    	sb.append(ValuesGenerator.toCodeString(elementType,this.inputVariables.get(i)));	
			    	if((i+1)<this.inputVariables.size())
			    		sb.append(",");
			    }
			    sb.append("}");
			    return sb.toString();
//		    return "array_of_" + this.elementType.getSimpleName() + "_of_size_" + this.length;
		  }


		  public Class<?> getOutputType() {
			    if (outputTypeCached == null) {
			      outputTypeCached = Array.newInstance(elementType, 0).getClass();
			    }
			    return outputTypeCached;
			  }
		  public String toParseableString(Map<String,Class<?>> classesToTest) {
			    return  elementType.getName()+"[]" + ref + "=" +elementType.getName() + "[" + Integer.toString(length) + "]";
			  }

		  @Override
			public Operation clone() {
				List<Object> cloneInputVariables=new ArrayList<Object>(inputVariables);
				final Copy_2_of_CreateArray clone =new Copy_2_of_CreateArray(ref,elementType,  length, cloneInputVariables);
				return clone;
			}

}
